home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Tech Arsenal 1
/
Tech Arsenal (Arsenal Computer).ISO
/
tek-04
/
ms_sh21s.zip
/
SH210
/
SRC
/
SH1.C
< prev
next >
Wrap
C/C++ Source or Header
|
1992-12-14
|
56KB
|
2,733 lines
/* MS-DOS SHELL - Main program, memory and variable management
*
* MS-DOS SHELL - Copyright (c) 1990,1,2 Data Logic Limited and Charles Forsyth
*
* This code is based on (in part) the shell program written by Charles
* Forsyth and is subject to the following copyright restrictions:
*
* 1. Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice is duplicated in the
* source form and the copyright notice in file sh6.c is displayed
* on entry to the program.
*
* 2. The sources (or parts thereof) or objects generated from the sources
* (or parts of sources) cannot be sold under any circumstances.
*
* $Header: /usr/users/istewart/src/shell/sh2.1/RCS/sh1.c,v 2.5 1992/12/14 10:54:56 istewart Exp $
*
* $Log: sh1.c,v $
* Revision 2.5 1992/12/14 10:54:56 istewart
* BETA 215 Fixes and 2.1 Release
*
* Revision 2.4 1992/11/06 10:03:44 istewart
* 214 Beta test updates
*
* Revision 2.3 1992/09/03 18:54:45 istewart
* Beta 213 Updates
*
* Revision 2.2 1992/07/16 14:33:34 istewart
* Beta 212 Baseline
*
* Revision 2.1 1992/07/10 10:52:48 istewart
* 211 Beta updates
*
* Revision 2.0 1992/04/13 17:39:09 Ian_Stewartson
* MS-Shell 2.0 Baseline release
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <errno.h>
#include <setjmp.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#include <limits.h>
#include <dirent.h>
/* OS2 or DOS, DEBUG MEMORY or normal malloc */
#ifdef OS2
# define INCL_DOSSESMGR
# define INCL_DOSMEMMGR
# define INCL_DOSPROCESS
# define INCL_WINSWITCHLIST
# include <os2.h>
# ifdef OS2_DOSALLOC
# ifdef DEBUG_MEMORY
# define RELEASE_MEMORY(x) if (DosFreeSeg (SELECTOROF ((x)))) \
fprintf(stderr, "Memory Release error\n");
# else
# define RELEASE_MEMORY(x) DosFreeSeg (SELECTOROF ((x)))
# endif
# else
# define RELEASE_MEMORY(x) free ((x))
# endif
#else
# include <dos.h>
# define RELEASE_MEMORY(x) free ((x))
#endif
#include <time.h>
#include "sh.h"
/*
* Structure of Malloced space to allow release of space nolonger required
* without having to know about it.
*/
typedef struct region {
struct region *next;
int area;
#ifdef DEBUG_MEMORY
size_t nbytes;
#endif
} s_region;
static struct region *MemoryAreaHeader = (s_region *)NULL;
/*
* default shell, search rules
*/
static char *shellname = "c:/bin/sh";
static char search[] = ";c:/bin;c:/usr/bin";
static char *ymail = "You have mail\n";
static char *ShellNameLiteral = "sh";
static char *tilde = "~";
static char LIT_OSmode[] = "OSMODE";
static char LIT_Dollar[] = "$";
static char *NOExit = "sh: ignoring attempt to leave lowest level shell\n";
static bool ProcessingDEBUGTrap = FALSE;
static bool ProcessingERRORTrap = FALSE;
static unsigned char ATOE_GFlags; /* Twalk GLOBAL flags */
static unsigned char ATNE_Function; /* Twalk GLOBAL flags */
/* Integer Variables */
static struct ShellVariablesInit {
char *Name; /* Name of variable */
int Status; /* Initial status */
char *CValue;
} InitialiseShellVariables[] = {
{ LIT_COLUMNS, STATUS_INTEGER, "80" },
{ HistorySizeVariable, STATUS_INTEGER, "100" },
{ LineCountVariable, STATUS_INTEGER, "1" },
{ LIT_LINES, STATUS_INTEGER, "25" },
{ OptIndVariable, STATUS_INTEGER, "1" },
{ RandomVariable, (STATUS_EXPORT | STATUS_INTEGER),
null },
{ SecondsVariable, (STATUS_EXPORT | STATUS_INTEGER),
null },
{ LIT_OSmode, (STATUS_EXPORT | STATUS_CANNOT_UNSET |
STATUS_INTEGER), null },
{ LIT_Dollar, (STATUS_CANNOT_UNSET | STATUS_INTEGER),
null },
{ LastWordVariable, STATUS_EXPORT, null },
{ PathLiteral, (STATUS_EXPORT | STATUS_CANNOT_UNSET),
search },
{ IFS, (STATUS_EXPORT | STATUS_CANNOT_UNSET),
" \t\n" },
{ PS1, (STATUS_EXPORT | STATUS_CANNOT_UNSET),
"%e$ " },
{ PS2, (STATUS_EXPORT | STATUS_CANNOT_UNSET),
"> " },
{ PS3, STATUS_EXPORT, "#? " },
{ PS4, STATUS_EXPORT, "+ " },
{ HomeVariableName, STATUS_EXPORT, null },
{ ShellVariableName, STATUS_EXPORT, null },
{ (char *)NULL, 0 }
};
/* Entry directory */
static char *Start_directory = (char *)NULL;
static time_t ShellStartTime = 0; /* Load time of shell */
static time_t SecondsOffset = 0; /* Offset for SECONDS */
/* Original Interrupt 24 address */
#ifndef OS2
static void (interrupt far *Orig_I24_V) (void);
#endif
#ifdef SIGQUIT
static void (*qflag)(int) = SIG_IGN;
#endif
/* Functions */
static char * near CheckClassExpression (char *, int, bool);
static bool near Initialise (char *);
static void near ExecuteNextCommand (void);
static void near CheckForMailArriving (void);
static void near Pre_Process_Argv (char **, int *);
static void near LoadGlobalVariableList (void);
static void near LoadTheProfileFiles (void);
static void near ConvertUnixPathToMSDOS (void);
static void near ClearUserPrompts (void);
static void near SecondAndRandomEV (char *, long);
static void near SetUpParameterEV (int, char **, char *);
static bool near AllowedToSetVariable (VariableList *);
static void near SetUpANumericValue (VariableList *, long, int);
static void near ProcessErrorExit (void);
static char * near SuppressSpacesZeros (VariableList *, char *);
static void AddToNewEnvironment (void *, VISIT, int);
static void AddToOldEnvironment (void *, VISIT, int);
static void DeleteEnvironment (void *, VISIT, int);
static int SearchVariable (void *, void *);
static void near CheckOPTIND (char *, long);
static void near CreateIntegerVariables (void);
#ifdef OS2
static void near CheckForTerminatedProcess (void);
#endif
#ifdef OS2
extern ushort _aenvseg; /* Environment seg */
extern ushort _acmdln; /* Command line offset */
#endif
/*
* The main program starts here
*/
void main (int argc, register char **argv)
{
int cflag = 0;
int sc;
char *name = *argv;
int (*iof)(IO_State *) = File_GetNextCharacter;
/* Load up various parts of the */
/* system */
bool OptionsRflag = Initialise (*argv);
bool OptionsXflag = FALSE; /* -x option from */
/* command line */
bool level0 = FALSE; /* Level zero (read profile) */
jmp_buf ReturnPoint;
/*
* This is to fix a funny in the Microsoft C 6 OS/2 compiler which
* mis-allocates something in the object file for sh6.c
*/
e.iop = iostack - 1;
#ifdef OS2
SetWindowName ();
#endif
/* Set up start time */
ShellStartTime = time ((time_t *)NULL);
/* Preprocess options to convert two character options of the form /x to
* -x. Some programs!!
*/
if (argc > 1)
Pre_Process_Argv (argv, &argc);
/* Save the start directory for when we exit */
Start_directory = getcwd ((char *)NULL, PATH_MAX + 4);
/* Process the options */
while ((sc = GetOptions (argc, argv,
"PRabc:defghijklmnopqrtsuvwxyz0", 0)) != EOF)
{
switch (sc)
{
case '0': /* Level 0 flag for DOS */
level0 = TRUE;
break;
case 'r': /* Restricted */
OptionsRflag = TRUE;
break;
case 'c': /* Command on line */
ClearUserPrompts ();
cflag = 1;
PUSHIO (aword, OptionArgument,
iof = Line_GetNextCharacter, null);
break;
case 'q': /* No quit ints */
#ifdef SIGQUIT
qflag = SIG_DFL;
#endif
break;
case 't': /* One command */
ClearVariableStatus (PS1, STATUS_EXPORT);
SetVariableFromString (PS1, null);
iof = FileLine_GetNextCharacter;
break;
case 'x':
OptionsXflag = TRUE;
break;
#ifndef OS2
case 'R':
Orig_I24_V = (void (far *)())NULL;
ChangeInitLoad = TRUE; /* Change load .ini pt. */
break;
#else
case 'P': /* Use real pipes */
GlobalFlags |= FLAGS_REALPIPES;
break;
#endif
case 'i': /* Set interactive */
InteractiveFlag = TRUE;
case 's': /* standard input */
default:
if (islower (sc))
FL_SET (sc);
}
}
argv += OptionIndex;
argc -= OptionIndex;
/* Execute one off command - disable prompts */
if ((iof == File_GetNextCharacter) && (argc > 0))
{
/* Open the file if necessary */
if (strcmp ((name = *argv), ShellOptionsVariable))
{
ClearUserPrompts ();
if (!AddToStackForExection (name))
{
PrintErrorMessage (LIT_Emsg, "cannot open script", name,
strerror (errno));
exit (1);
}
}
}
/* Load terminal I/O structure if necessary and load the history file */
if (IOStackPosition (0) & IOSTACK_OUTSIDE)
{
PUSHIO (afile, 0, iof, null);
if (isatty (0) && isatty (1) && !cflag)
{
FL_SET ('s');
PrintVersionNumber (stderr);
InteractiveFlag = TRUE;
#ifndef NO_HISTORY
HistoryEnabled = TRUE;
LoadHistory ();
Configure_Keys ();
#endif
}
}
/* Set up the $- variable */
SetShellSwitches ();
#ifdef SIGQUIT
signal (SIGQUIT, qflag);
#endif
/* Read profile ? */
if (((name != (char *)NULL) && (*name == '-')) || level0)
LoadTheProfileFiles ();
/* Set up signals */
if (InteractiveFlag)
signal (SIGTERM, TerminateSignalled);
/* Return point */
if (setjmp (FailReturnPoint = ReturnPoint))
ExitTheShell (FALSE);
signal (SIGINT, InterruptSignalled);
/* Load any parameters */
SetUpParameterEV (argc, argv, name);
/* If the xflag was set on the command line, Mark the lowest level IO
* structure to restore it.
*/
if (OptionsXflag)
iostack[0].TaskType |= XRESET_XF;
if (OptionsRflag)
iostack[0].TaskType |= XRESET_RF;
/* If we are at IO stack zero, output the prompt */
/* Execute the command loop */
while (1)
{
/* Restart point for interrupts */
setjmp (FailReturnPoint = ReturnPoint);
/* If this is the first time, check to see if we need to read the ENV file */
if ((IOStackPosition (0) & IOSTACK_FIRST) && FirstReadFromUser)
{
FirstReadFromUser = FALSE;
AddToStackForExection (GetVariableAsString (ENVVariable, FALSE));
}
/* If we are at IO stack zero, output the prompt */
if (InteractiveFlag && (IOStackPosition (0) & IOSTACK_FIRST))
{
/* Set up a few things for console input - cursor, mail prompt etc */
PositionCursorInColumnZero ();
CheckForMailArriving ();
#ifdef OS2
CheckForTerminatedProcess ();
#endif
LastUserPrompt = PS1;
CloseAllHandlers (); /* Clean up any open shell files */
}
/* Read the next command and process it */
ExecuteNextCommand ();
MemoryAreaLevel = 0;
}
}
/*
* Set up the value of $-
*/
void SetShellSwitches (void)
{
register char *cp, c;
char m['z' - 'a' + 1];
for (cp = m, c = 'a'; c <= 'z'; ++c)
{
if (FL_TEST (c))
*(cp++) = c;
}
*cp = 0;
SetVariableFromString (ShellOptionsVariable, m);
}
/* Execute a command */
static void near ExecuteNextCommand (void)
{
register int i;
jmp_buf ReturnPoint;
C_Op *outtree = (C_Op *)NULL;
/* Exit any previous environments */
while (e.oenv)
QuitCurrentEnvironment ();
/* initialise space */
MemoryAreaLevel = 1;
FreeAllHereFiles (MemoryAreaLevel);
ReleaseMemoryArea (MemoryAreaLevel);
WordListBlock = (Word_B *)NULL;
IOActionBlock = (Word_B *)NULL;
e.ErrorReturnPoint = (int *)NULL;
e.cline = GetAllocatedSpace (LINE_MAX);
e.eline = e.cline + LINE_MAX - 5;
e.linep = e.cline;
AllowMultipleLines = 0;
InParser = TRUE;
SW_intr = 0;
ProcessingEXECCommand = FALSE;
flushall (); /* Clear output */
/* Get the line and process it */
if (setjmp (FailReturnPoint = ReturnPoint) ||
((outtree = BuildParseTree ()) == (C_Op *)NULL) || SW_intr)
{
/* If interrupt occured, remove current Input stream */
if (SW_intr && (IOBasePosition () == IOSTACK_INSIDE))
CloseUpIOStack (e.iop--, TRUE);
/* Quit all environments */
while (e.oenv)
QuitCurrentEnvironment ();
ScrapHereList ();
if (!InteractiveFlag && SW_intr)
ExitTheShell (FALSE);
/* Exit */
InParser = FALSE;
SW_intr = 0;
return;
}
/* Ok - reset some variables and then execute the command tree */
InParser = FALSE;
Break_List = (Break_C *)NULL;
Return_List = (Break_C *)NULL;
SShell_List = (Break_C *)NULL;
SW_intr = 0;
ProcessingEXECCommand = FALSE;
FlushHistoryBuffer (); /* Save history */
/* Set execute function recursive level and the SubShell count to zero */
Execute_stack_depth = 0;
/* Set up Redirection IO (Saved) array and SubShell Environment information */
NSave_IO_E = 0; /* Number of entries */
MSave_IO_E = 0; /* Max Number of entries */
NSubShells = 0; /* Number of entries */
MSubShells = 0; /* Max Number of entries */
CurrentFunction = (FunctionList *)NULL;
ProcessingDEBUGTrap = FALSE;
ProcessingERRORTrap = FALSE;
/* Ok - if we wail, we need to clean up the stacks */
if ((setjmp (FailReturnPoint = ReturnPoint) == 0) && !FL_TEST ('n'))
ExecuteParseTree (outtree, NOPIPE, NOPIPE, 0);
/* Make sure the I/O and environment are back at level 0 and then clear them */
Execute_stack_depth = 0;
ClearExtendedLineFile ();
if (NSubShells != 0)
DeleteGlobalVariableList ();
if (NSave_IO_E)
RestoreStandardIO (0, TRUE);
if (MSubShells)
ReleaseMemoryCell ((void *)SubShells);
if (MSave_IO_E)
ReleaseMemoryCell ((void *)SSave_IO);
/* Check for interrupts */
if (!InteractiveFlag && SW_intr)
{
ProcessingEXECCommand = FALSE;
ExitTheShell (FALSE);
}
/* Run any traps that are required */
if ((i = InterruptTrapPending) != 0)
{
InterruptTrapPending = 0;
RunTrapCommand (i);
}
}
/*
* Terminate current environment with an error
*/
void TerminateCurrentEnvironment (void)
{
flushall ();
longjmp (FailReturnPoint, 1);
/* NOTREACHED */
}
/*
* Exit the shell
*/
void ExitTheShell (bool ReturnRequired)
{
flushall ();
if (ProcessingEXECCommand)
TerminateCurrentEnvironment ();
#ifndef OS2
if (Orig_I24_V == (void (far *)())NULL)
{
fputs (NOExit, stderr);
if (!ReturnRequired)
TerminateCurrentEnvironment ();
}
#endif
/* Clean up */
ScrapHereList ();
FreeAllHereFiles (1);
/* Trap zero on exit */
RunTrapCommand (0);
/* Dump history on exit */
#ifndef NO_HISTORY
if (InteractiveFlag && isatty(0))
DumpHistory ();
#endif
CloseAllHandlers ();
/* Clear swap file if necessary */
#ifndef OS2
ClearSwapFile ();
#endif
/* If this is a command only - restore the directory because DOS doesn't
* and the user might expect it
*/
if (Start_directory != (char *)NULL)
RestoreCurrentDirectory (Start_directory);
/* If this happens during startup - we restart */
#ifndef OS2
if (Orig_I24_V == (void (far *)())NULL)
return;
#endif
/* Exit - hurray */
exit (ExitStatus);
/* NOTREACHED */
}
/*
* Output warning message
*/
int PrintWarningMessage (char *fmt, ...)
{
va_list ap;
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
ExitStatus = -1;
/* If leave on error - exit */
if (FL_TEST ('e'))
ExitTheShell (FALSE);
va_end (ap);
return 1;
}
/*
* Shell error message
*/
void ShellErrorMessage (char *fmt, ...)
{
va_list ap;
/* Error message processing */
if (e.iop->FileName == null)
fputs ("sh: ", stderr);
else
fprintf (stderr, "%s: at line %d, ", e.iop->FileName, e.iop->LineCount);
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
fputc (CHAR_NEW_LINE, stderr);
ProcessErrorExit ();
va_end (ap);
}
/*
* Output error message
*/
void PrintErrorMessage (char *fmt, ...)
{
va_list ap;
/* Error message processing */
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
ProcessErrorExit ();
va_end (ap);
}
/*
* Common processing for PrintErrorMessage and ShellError Message
*/
static void near ProcessErrorExit (void)
{
ExitStatus = -1;
if (FL_TEST ('e'))
ExitTheShell (FALSE);
/* Error processing */
if (FL_TEST ('n'))
return;
/* If not interactive - exit */
if (!InteractiveFlag)
ExitTheShell (FALSE);
if (e.ErrorReturnPoint)
longjmp (e.ErrorReturnPoint, 1);
/* CloseAllHandlers (); Removed - caused problems. There may be problems
* remaining with files left open?
*/
while (IOStackPosition (0) & IOSTACK_INSIDE)
CloseUpIOStack (e.iop--, TRUE);
e.iop = e.iobase = iostack;
}
/*
* Create or delete a new environment. If f is set, delete the environment
*/
bool CreateNewEnvironment (int ErrorCode)
{
register ShellFileEnvironment *ep;
/* Delete environment? */
if (ErrorCode)
{
QuitCurrentEnvironment ();
return TRUE;
}
/* Create a new environment */
if ((ep = (ShellFileEnvironment *)
GetAllocatedSpace (sizeof (ShellFileEnvironment)))
== (ShellFileEnvironment *)NULL)
{
while (e.oenv)
QuitCurrentEnvironment ();
TerminateCurrentEnvironment ();
}
*ep = e;
e.eof_p = FALSE; /* Disable EOF processing */
e.oenv = ep;
e.ErrorReturnPoint = ErrorReturnPoint;
return FALSE;
}
/*
* Exit the current environment successfully
*/
void QuitCurrentEnvironment (void)
{
register ShellFileEnvironment *ep;
register int fd;
/* Restore old environment, delete the space and close any files opened in
* this environment
*/
if ((ep = e.oenv) != (ShellFileEnvironment *)NULL)
{
/* Get the files used in this environment to close */
fd = e.FirstAvailableFileHandler;
e = *ep;
ReleaseMemoryCell ((void *)ep);
while (--fd >= e.FirstAvailableFileHandler)
S_close (fd, TRUE);
}
}
/*
* Is character c in s?
*/
bool any (register char c, register char *s)
{
while (*s)
{
if (*(s++) == c)
return TRUE;
}
return FALSE;
}
/*
* Convert binary to ascii
*/
char *IntegerToString (register int n)
{
static char nt[10];
sprintf (nt, "%u", n);
return nt;
}
/*
* SIGINT interrupt processing
*/
void InterruptSignalled (int signo)
{
/* Restore signal processing and set SIGINT detected flag */
signal (signo, InterruptSignalled);
SW_intr = 1;
/* Zap the swap file, just in case it got corrupted */
#ifndef OS2
S_close (SW_fp, TRUE);
ClearSwapFile ();
#endif
/* Are we talking to the user? Yes - check in parser */
if (InteractiveFlag)
{
if (InParser)
fputc (CHAR_NEW_LINE, stderr);
/* Abandon processing */
TerminateCurrentEnvironment ();
}
/* No - exit */
else
{
ProcessingEXECCommand = FALSE;
ExitStatus = 1;
ExitTheShell (FALSE);
}
}
/*
* Grap some space and check for an error
*/
char *GetAllocatedSpace (size_t n)
{
register char *cp;
if ((cp = AllocateMemoryCell (n)) == (char *)NULL)
PrintErrorMessage (BasicErrorMessage, ShellNameLiteral, Outofmemory1);
return cp;
}
/*
* Save a string in a given area
*/
char *StringSave (register char *s)
{
register char *cp;
if ((cp = GetAllocatedSpace (strlen (s) + 1)) != (char *)NULL)
{
SetMemoryAreaNumber ((void *)cp, 0);
return strcpy (cp, s);
}
return null;
}
/*
* Duplicate at current Memory level
*/
char *StringCopy (register char *s)
{
register char *cp;
if ((cp = GetAllocatedSpace (strlen (s) + 1)) != (char *)NULL)
return strcpy (cp, s);
return null;
}
/*
* trap handling - Save signal number and restore signal processing
*/
void TerminateSignalled (register int i)
{
if (i == SIGINT) /* Need this because swapper sets it */
{
SW_intr = 0;
/* Zap the swap file, just in case it got corrupted */
#ifndef OS2
S_close (SW_fp, TRUE);
ClearSwapFile ();
#endif
}
InterruptTrapPending = i;
signal (i, TerminateSignalled);
}
/*
* Execute a trap command
*
* 0 - exit trap
* -1 - Debug Trap
* -2 - Error Trap
*/
void RunTrapCommand (int i)
{
char *trapstr;
char tval[10];
char *tvalp = tval;
/* Check for special values and recursion */
if (i == -1)
{
tvalp = Trap_DEBUG;
if (ProcessingDEBUGTrap)
return;
ProcessingDEBUGTrap = TRUE;
}
/* Error trap */
else if (i == -2)
{
tvalp = Trap_ERR;
if (ProcessingERRORTrap)
return;
ProcessingERRORTrap = TRUE;
}
else
{
*tval = '~';
itoa (i, tval + 1, 10);
}
if ((trapstr = GetVariableAsString (tvalp, FALSE)) == null)
return;
/* If signal zero, save a copy of the trap value and then delete the trap */
if (i == 0)
{
trapstr = StringCopy (trapstr);
UnSetVariable (tval, TRUE);
}
RUN (aword, trapstr, Line_GetNextCharacter, TRUE, null, (char **)NULL);
ProcessingDEBUGTrap = FALSE;
ProcessingERRORTrap = FALSE;
}
/*
* Find the given name in the dictionary and return its value. If the name was
* not previously there, enter it now and return a null value.
*/
VariableList *LookUpVariable (register char *name, bool cflag)
{
register VariableList *vp;
VariableList **vpp;
register int c;
static VariableList dummy;
void (*save_signal)(int);
/* Set up the dummy variable */
memset (&dummy, 0, sizeof (VariableList));
dummy.name = name;
dummy.status = STATUS_READONLY;
dummy.value = null;
/* If digit string - use the dummy to return the value */
if (isdigit (*name))
{
for (c = 0; isdigit (*name) && (c < 1000); name++)
c = c * 10 + *name - '0';
dummy.value = (c <= ParameterCount) ? ParameterArray[c] : null;
return &dummy;
}
/* Look up in list */
vpp = (VariableList **)tfind (name, &VariableTree, FindVariable);
/* If we found it, return it */
if (vpp != (VariableList **)NULL)
{
vp = *vpp;
/* Special processing for SECONDS and RANDOM */
if (!strcmp (name, SecondsVariable) &&
!(DisabledVariables & DISABLE_SECONDS))
SetUpANumericValue (vp, time ((time_t *)NULL) - ShellStartTime +
SecondsOffset, 10);
else if (!strcmp (name, RandomVariable) &&
!(DisabledVariables & DISABLE_RANDOM))
SetUpANumericValue (vp, (long)rand(), 10);
return vp;
}
/* If we don't want to create it, return a dummy */
dummy.status |= STATUS_NOEXISTANT;
if (!cflag)
return &dummy;
/* Create a new variable. If no memory, use the dummy */
dummy.name = null;
if ((vp = (VariableList *)GetAllocatedSpace (sizeof (VariableList)))
== (VariableList *)NULL)
return &dummy;
if ((vp->name = StringCopy (name)) == null)
{
ReleaseMemoryCell ((void *)vp);
return &dummy;
}
/* Save signals */
save_signal = signal (SIGINT, SIG_IGN);
/* Add to the tree */
if (tsearch (vp, &VariableTree, SearchVariable) == (void *)NULL)
{
ReleaseMemoryCell ((void *)vp->name);
ReleaseMemoryCell ((void *)vp);
return &dummy;
}
/* OK Added OK - set up memory */
SetMemoryAreaNumber ((void *)vp, 0);
SetMemoryAreaNumber ((void *)vp->name, 0);
vp->value = null;
/* Restore signals */
signal (SIGINT, save_signal);
return vp;
}
/*
* TWALK compare functions for VARIABLE tree
*
* Note: We know about these two function, so we know the key is always
* the first parameter. So we only pass the char * not the FunctionList
* pointer.
*/
int FindVariable (void *key1, void *key2)
{
return strcmp (key1, ((VariableList *)key2)->name);
}
/*
* TSEARCH - Search the VARIABLE TREE for an entry
*/
static int SearchVariable (void *key1, void *key2)
{
return strcmp (((VariableList *)key1)->name, ((VariableList *)key2)->name);
}
/*
* Execute an assignment. If a valid assignment, load it into the variable
* list.
*/
bool AssignVariableFromString (register char *s)
{
register char *cp;
/* Ignore if not valid environment variable - check alpha and equals */
if (IsValidVariableName (s) != '=')
return FALSE;
/* Assign the value */
*(cp = strchr (s, '=')) = 0;
SetVariableFromString (s, ++cp);
SecondAndRandomEV (s, atol (cp));
return TRUE;
}
/*
* Duplicate the Variable List for a Subshell
*
* Create a new Var_list environment for a Sub Shell
*/
int CreateGlobalVariableList (unsigned char Function)
{
int i;
S_SubShell *sp;
for (sp = SubShells, i = 0; (i < NSubShells) &&
(SubShells[i].depth < Execute_stack_depth);
i++);
/* If depth is greater or equal to the Execute_stack_depth - we should panic
* as this should not happen. However, for the moment, I'll ignore it
*/
if (NSubShells == MSubShells)
{
sp = (S_SubShell *) GetAllocatedSpace ((MSubShells + SSAVE_IO_SIZE) *
sizeof (S_SubShell));
/* Check for error */
if (sp == (S_SubShell *)NULL)
return -1;
/* Save original data */
if (MSubShells != 0)
{
memcpy (sp, SubShells, sizeof (S_SubShell) * MSubShells);
ReleaseMemoryCell ((void *)SubShells);
}
SetMemoryAreaNumber ((void *)sp, 0);
SubShells = sp;
MSubShells += SSAVE_IO_SIZE;
}
/* Save the depth and the old Variable Tree value */
sp = &SubShells[NSubShells++];
sp->OldVariableTree = VariableTree;
sp->depth = Execute_stack_depth;
sp->GFlags = GlobalFlags | Function;
sp->Eflags = flags;
VariableTree = (void *)NULL;
/* Duplicate the old Variable list */
ATNE_Function = Function;
twalk (sp->OldVariableTree, AddToNewEnvironment);
/* Reset global values */
LoadGlobalVariableList ();
return 0;
}
/*
* TWALK - add to new environment
*/
static void AddToNewEnvironment (void *key, VISIT visit, int level)
{
VariableList *vp = *(VariableList **)key;
VariableList *vp1;
if ((visit == postorder) || (visit == leaf))
{
/* For functions, do not copy the traps */
if (ATNE_Function && (*vp->name == '~') && vp->name[1])
return;
/* Create a new entry */
vp1 = LookUpVariable (vp->name, TRUE);
if ((!(vp->status & STATUS_INTEGER)) && (vp->value != null))
vp1->value = StringSave (vp->value);
/* Copy some flags */
vp1->status = vp->status;
vp1->nvalue = vp->nvalue;
vp1->base = vp->base;
vp1->width = vp->width;
}
}
/*
* Delete a SubShell environment and restore the original
*/
void DeleteGlobalVariableList (void)
{
int j;
S_SubShell *sp;
VariableList *vp;
void (*save_signal)(int);
for (j = NSubShells; j > 0; j--)
{
sp = &SubShells[j - 1];
if (sp->depth < Execute_stack_depth)
break;
/* Reduce number of entries */
--NSubShells;
/* Disable signals */
save_signal = signal (SIGINT, SIG_IGN);
/* Restore the previous level information */
vp = VariableTree;
VariableTree = sp->OldVariableTree;
GlobalFlags = (unsigned char)(sp->GFlags & ~FLAGS_FUNCTION);
flags = sp->Eflags;
/* Release the space */
ATOE_GFlags = sp->GFlags;
twalk (vp, AddToOldEnvironment);
twalk (vp, DeleteEnvironment);
/* Restore signals */
signal (SIGINT, save_signal);
LoadGlobalVariableList ();
}
}
/*
* TWALK - delete old environment tree
*/
static void DeleteEnvironment (void *key, VISIT visit, int level)
{
VariableList *vp = *(VariableList **)key;
if ((visit == endorder) || (visit == leaf))
{
if (vp->value == null)
ReleaseMemoryCell ((void *)vp->value);
ReleaseMemoryCell ((void *)vp->name);
ReleaseMemoryCell ((void *)vp);
}
}
/*
* TWALK - Transfer Current Environment to the Old one
*/
static void AddToOldEnvironment (void *key, VISIT visit, int level)
{
VariableList *vp = *(VariableList **)key;
VariableList *vp1;
if ((visit == postorder) || (visit == leaf))
{
/* Skip local variables and traps */
if ((ATOE_GFlags & FLAGS_FUNCTION) && (!(vp->status & STATUS_LOCAL)) &&
(((*vp->name != '~') || !vp->name[1])))
{
/* Get the entry in the old variable list and update it with the new
* parameters
*/
vp1 = LookUpVariable (vp->name, TRUE);
if (vp1->value != null)
ReleaseMemoryCell ((void *)vp1->value);
vp1->value = vp->value;
vp->value = null; /* Stop releaseing this as its tx */
vp1->status = vp->status;
vp1->nvalue = vp->nvalue;
vp1->base = vp->base;
vp1->width = vp->width;
}
}
}
/*
* Load GLobal Var List values
*/
static void near LoadGlobalVariableList (void)
{
CurrentDirectory = LookUpVariable (tilde, TRUE);
RestoreCurrentDirectory (CurrentDirectory->value);
}
/*
* Match a pattern as in sh(1). Enhancement to handle prefix processing
*
* IgnoreCase - ignore case on comparisions.
* end - end of match in 'string'.
* mode - mode for match processing - see GM_ flags in sh.h
*/
bool GeneralPatternMatch (register char *string, register char *pattern,
bool IgnoreCase, char **end, int mode)
{
register int string_c, pattern_c;
char *save_end;
if ((string == (char *)NULL) || (pattern == (char *)NULL))
return FALSE;
while ((pattern_c = *(pattern++)) != '\0')
{
string_c = *(string++);
switch (pattern_c)
{
case CHAR_OPEN_BRACKETS: /* Class expression */
if ((pattern =
CheckClassExpression (pattern, string_c, IgnoreCase))
== (char *)NULL)
return FALSE;
break;
case '?': /* Match any character */
if (string_c == 0)
return FALSE;
break;
case '*': /* Match as many as possible */
--string;
save_end = (char *)NULL;
do
{
if (!*pattern ||
GeneralPatternMatch (string, pattern, IgnoreCase, end,
mode))
{
if (mode == GM_LONGEST)
save_end = *end;
else
return TRUE;
}
} while (*(string++));
if (end != (char **)NULL)
*end = save_end;
return (save_end == (char *)NULL) ? FALSE : TRUE;
case '\\': /* Escape the next value */
if (*pattern)
pattern_c = *(pattern++);
default: /* Match */
if (IgnoreCase)
{
string_c = tolower (string_c);
pattern_c = tolower (pattern_c);
}
if (string_c != pattern_c)
return FALSE;
}
}
if (end != (char **)NULL)
{
*end = string;
return TRUE;
}
return (*string == 0) ? TRUE : FALSE;
}
/*
* Process a class expression - []
*/
static char * near CheckClassExpression (register char *pattern,
register int string_c, bool IgnoreCase)
{
register int llimit_c, ulimit_c, not, found;
/* Exclusive or inclusive class */
if ((not = *pattern == CHAR_NOT) != 0)
pattern++;
found = not;
do
{
if (!*pattern)
return (char *)NULL;
/* Get the next character in class, converting to lower case if necessary */
llimit_c = IgnoreCase ? tolower (*pattern) : *pattern;
/* If this is a range, get the end of range character */
if ((*(pattern + 1) == '-') && (*(pattern + 2) != CHAR_CLOSE_BRACKETS))
{
ulimit_c = IgnoreCase ? tolower (*(pattern + 2)) : *(pattern + 2);
pattern++;
}
else
ulimit_c = llimit_c;
/* Is the current character in the class? */
if ((llimit_c <= string_c) && (string_c <= ulimit_c))
found = !not;
} while (*(++pattern) != CHAR_CLOSE_BRACKETS);
return found ? pattern + 1 : (char *)NULL;
}
/*
* Suffix processing - find the longest/shortest suffix.
*/
bool SuffixPatternMatch (register char *string, register char *pattern,
char **start, int mode)
{
char *save_start = (char *)NULL;
/* Scan the string, looking for a match to the end */
while (*string)
{
if (GeneralPatternMatch (string, pattern, FALSE, (char **)NULL, GM_ALL))
{
/* If longest, stop here */
if (mode == GM_LONGEST)
{
*start = string;
return TRUE;
}
/* Save the start of the shortest string so far and continue */
save_start = string;
}
++string;
}
return ((*start = save_start) == (char *)NULL) ? FALSE : TRUE;
}
/*
* Get a string in a malloced area
*/
char *AllocateMemoryCell (size_t nbytes)
{
s_region *np;
void (*save_signal)(int);
#ifdef OS2_DOSALLOC
SEL sel;
#endif
if (nbytes == 0)
abort (); /* silly and defeats the algorithm */
/* Grab some space */
#ifdef OS2_DOSALLOC
if (DosAllocSeg (nbytes + sizeof (s_region), &sel, SEG_NONSHARED))
{
errno = ENOMEM;
return (char *)NULL;
}
np = (s_region *)MAKEP (sel, 0);
memset (np, 0, nbytes + sizeof (s_region));
#else
if ((np = (s_region *)calloc (nbytes + sizeof (s_region), 1))
== (s_region *)NULL)
{
errno = ENOMEM;
return (char *)NULL;
}
#endif
/* Disable signals */
save_signal = signal (SIGINT, SIG_IGN);
/* Link into chain */
np->next = MemoryAreaHeader;
np->area = MemoryAreaLevel;
#ifdef DEBUG_MEMORY
np->nbytes = nbytes;
#endif
MemoryAreaHeader = np;
/* Restore signals */
signal (SIGINT, save_signal);
return ((char *)np) + sizeof (s_region);
}
/*
* Free a string in a malloced area
*/
void ReleaseMemoryCell (void *s)
{
s_region *cp = MemoryAreaHeader;
s_region *lp = (s_region *)NULL;
s_region *sp = (s_region *)((char *)s - sizeof (s_region));
void (*save_signal)(int);
/* Disable signals */
save_signal = signal (SIGINT, SIG_IGN);
/* Find the string in the chain */
if (s != (char *)NULL)
{
while (cp != (s_region *)NULL)
{
if (cp != sp)
{
lp = cp;
cp = cp->next;
continue;
}
/* First in chain ? */
else if (lp == (s_region *)NULL)
MemoryAreaHeader = cp->next;
/* Delete the current entry and relink */
else
lp->next = cp->next;
RELEASE_MEMORY (cp);
break;
}
}
/* Restore signals */
signal (SIGINT, save_signal);
}
/*
* Check for memory leaks with a dump
*/
#ifdef DEBUG_MEMORY
void DumpMemoryCells (int status)
{
s_region *cp = MemoryAreaHeader;
size_t i;
char buffer[17];
char *sp;
/* Find the string in the chain */
while (cp != (s_region *)NULL)
{
fprintf (stderr, "Segment 0x%.8lx Area %5d Length %5d Link 0x%.8lx\n",
cp, cp->area, cp->nbytes, cp->next);
memset (buffer, ' ', 17);
buffer[16] = 0;
sp = ((char *)cp) + sizeof (s_region);
for (i = 0; i < (((cp->nbytes - 1)/16) + 1) * 16; i++)
{
if (i >= cp->nbytes)
{
fputs (" ", stderr);
buffer [i % 16] = ' ';
}
else
{
fprintf (stderr, "%.2x ", *sp & 0x0ff);
buffer [i % 16] = (char)(isprint (*sp) ? *sp : '.');
}
if (i % 16 == 15)
fprintf (stderr, " [%s]\n", buffer);
sp++;
}
fputc ('\n', stderr);
cp = cp->next;
}
#undef exit
exit (status);
#define exit(x) DumpMemoryCells (x)
}
#endif
/*
* Autodelete space nolonger required. Ie. Free all the strings in a malloced
* area
*/
void ReleaseMemoryArea (register int a)
{
s_region *cp = MemoryAreaHeader;
s_region *lp = (s_region *)NULL;
void (*save_signal)(int);
/* Disable signals */
save_signal = signal (SIGINT, SIG_IGN);
while (cp != (s_region *)NULL)
{
/* Is the area number less than that specified - yes, continue */
if (cp->area < a)
{
lp = cp;
cp = cp->next;
}
/* OK - delete the area. Is it the first in chain ? Yes, delete, relink
* and update start location
*/
else if (lp == (s_region *)NULL)
{
lp = cp;
cp = cp->next;
MemoryAreaHeader = cp;
RELEASE_MEMORY (lp);
lp = (s_region *)NULL;
}
/* Not first, delete the current entry and relink */
else
{
lp->next = cp->next;
RELEASE_MEMORY (cp);
cp = lp->next;
}
}
/* Restore signals */
signal (SIGINT, save_signal);
}
/*
* Set the area number for a malloced string. This allows autodeletion of
* space that is nolonger required.
*/
void SetMemoryAreaNumber (void *cp, int a)
{
s_region *sp = (s_region *)((char *)cp - sizeof (s_region));
if (cp != (void *)NULL)
sp->area = a;
}
/*
* Get the area number for a malloced string
*/
int GetMemoryAreaNumber (void *cp)
{
s_region *sp = (s_region *)((char *)cp - sizeof (s_region));
return sp->area;
}
/* Output one of the Prompt. We save the prompt for the history part of
* the program
*/
void OutputUserPrompt (char *s)
{
struct tm *tm;
time_t xtime = time ((time_t *)NULL);
int i;
char buf[PATH_MAX + 4];
if (LastUserPrompt != (char *)NULL)
{
LastUserPrompt = s; /* Save the Last prompt id */
s = GetVariableAsString (s, TRUE); /* Get the string value */
}
else
s = LastUserPrompt1;
tm = localtime (&xtime);
while (*s)
{
/* If a format character, process it */
if (*s == '%')
{
s++;
*s = (char)tolower(*s);
if (*s == '%')
putchar ('%');
else
{
*buf = 0;
switch (*(s++))
{
case 'e': /* Current event number */
if (HistoryEnabled)
itoa (Current_Event + 1, buf, 10);
break;
case 't': /* time */
sprintf (buf,"%.2d:%.2d", tm->tm_hour, tm->tm_min);
break;
case 'd': /* date */
sprintf (buf, "%.3s %.2d-%.2d-%.2d",
&"SunMonTueWedThuFriSat"[tm->tm_wday * 3],
tm->tm_mday, tm->tm_mon + 1,
tm->tm_year % 100);
break;
case 'p': /* directory */
case 'n': /* default drive */
strcpy (buf, CurrentDirectory->value);
if (*(s - 1) == 'n')
buf[1] = 0;
break;
case 'v': /* version */
#ifdef OS2
sprintf (buf, "OS/2 %.2d:%.2d", _osmajor / 10,
_osminor);
#else
sprintf (buf, "MS-DOS %.2d:%.2d", _osmajor, _osminor);
#endif
break;
}
/* Output the string */
fputs (buf, stdout);
}
}
/* Escaped character ? */
else if (*s == '\\')
{
++s;
if ((i = ProcessOutputMetaCharacters (&s)) == -1)
i = 0;
putchar ((char)i);
}
else
putchar (*(s++));
}
fflush (stdout);
}
/*
* Get the current path in UNIX format and save it in the environment
* variable $~
*/
void GetCurrentDirectory (void)
{
char ldir[PATH_MAX + 6];
char *CurrentPWDValue; /* Current directory */
getcwd (ldir, PATH_MAX + 4);
ldir[PATH_MAX + 5] = 0;
/* Convert to Unix format */
#ifdef OS2
PATH_TO_UNIX (ldir);
if (!IsHPFSFileSystem (ldir))
strlwr (ldir);
#else
PATH_TO_UNIX (strlwr (ldir));
#endif
/* Save in environment */
SetVariableFromString (tilde, ldir);
CurrentDirectory = LookUpVariable (tilde, TRUE);
/* If we have changed directory, set PWD and OLDPWD */
if (strcmp (CurrentPWDValue = GetVariableAsString (PWDVariable, FALSE),
ldir))
{
SetVariableFromString (OldPWDVariable, CurrentPWDValue);
SetVariableFromString (PWDVariable, ldir);
}
}
/*
* Initialise the shell and Patch up various parts of the system for the
* shell. At the moment, we modify the ctype table so that _ is an upper
* case character.
*/
static bool near Initialise (char *name)
{
register char *s, *s1;
char **ap;
bool OptionsRflag = FALSE;
/* Patch the ctype table as a cheat */
(_ctype+1)['_'] |= _UPPER;
/* Get original interrupt 24 address and set up our new interrupt 24
* address
*/
#ifndef OS2
Orig_I24_V = _dos_getvect (0x24);
_dos_setvect (0x24, SW_Int24);
#endif
/* Create the integer variables, in case they are loaded from the
* environment
*/
CreateIntegerVariables ();
/* Load the environment into our structures */
if ((ap = environ) != (char **)NULL)
{
for (ap = environ; *ap != (char *)NULL; ap++)
{
/* Set up any variables. Note there is an assumption that
* AssignVariableFromString sets the equals sign to 0, hiding the value;
*/
if (!strncmp ("SECONDS=", *ap, 8))
continue;
if (AssignVariableFromString (*ap))
SetVariableStatus (*ap, STATUS_EXPORT);
}
}
/* Change COMSPEC to unix format for execution */
PATH_TO_UNIX (GetVariableAsString (ComspecVariable, FALSE));
SetVariableStatus (ComspecVariable, STATUS_CONVERT_MSDOS);
/* Zap all files */
CloseAllHandlers ();
MemoryAreaLevel = 1;
/* Get the current directory */
GetCurrentDirectory ();
/* Initialise the getopts command */
ResetGetoptsValues (TRUE);
/* Set up SHELL variable. First check for a restricted shell. Check the
* restricted shell
*/
SetVariableFromString (LastWordVariable, name);
if ((s = strrchr (name, CHAR_UNIX_DIRECTORY)) == (char *)NULL)
s = name;
else
s++;
if ((s1 = strchr (s, '.')) != (char *)NULL)
*s1 = 0;
if (!strcmp (s, "rsh"))
OptionsRflag = TRUE;
/* Has the program name got a .exe extension - Yes probably DOS 3+. So
* save it as the Shell name
*/
if (s1 != (char *)NULL)
{
if ((stricmp (s1 + 1, EXEExtension + 1) == 0) &&
(GetVariableAsString (ShellVariableName, FALSE) == null))
SetVariableFromString (ShellVariableName, name);
*s1 = '.';
}
/* Default if necessary */
if (GetVariableAsString (ShellVariableName, FALSE) == null)
SetVariableFromString (ShellVariableName, shellname);
PATH_TO_UNIX (s1 = GetVariableAsString (ShellVariableName, FALSE));
/* Check for restricted shell */
if ((s = strrchr (s1, CHAR_UNIX_DIRECTORY)) == (char *)NULL)
s = s1;
else
s++;
if (*s == 'r')
OptionsRflag = TRUE;
/* Set up home directory */
if (GetVariableAsString (HomeVariableName, FALSE) == null)
{
if ((s = GetVariableAsString ("INIT", FALSE)) == null)
s = CurrentDirectory->value;
SetVariableFromString (HomeVariableName, s);
}
/* Set up OS Mode */
SetVariableFromNumeric (LIT_OSmode, (long)_osmode);
/* Set up history file location */
SetVariableFromNumeric (LIT_Dollar, (long)getpid ());
LoadGlobalVariableList ();
PATH_TO_UNIX (GetVariableAsString (PathLiteral, FALSE));
return OptionsRflag;
}
/*
* Mail Check processing. Every $MAILCHECK seconds, we check either $MAIL
* or $MAILPATH to see if any file has changed its modification time since
* we last looked. In $MAILCHECK, the files are separated by semi-colon (;).
* If the filename contains a %, the string following the % is the message
* to display if the file has changed.
*/
static void near CheckForMailArriving (void)
{
int delay = (int)GetVariableAsNumeric (MailCheckVariable);
char *mail = GetVariableAsString ("MAIL", FALSE);
char *mailp = GetVariableAsString ("MAILPATH", FALSE);
static time_t last = 0L;
time_t current = time ((time_t *)NULL);
struct stat st;
char *cp, *sp, *ap;
/* Have we waited long enough */
if (((current - last) < delay) || (DisabledVariables & DISABLE_MAILCHECK))
return;
/* Yes - Check $MAILPATH. If it is defined, process it. Otherwise, use
* $MAIL
*/
if (mailp != null)
{
/* Check MAILPATH */
sp = mailp;
/* Look for the next separator */
while ((cp = strchr (sp, ';')) != (char *)NULL)
{
*cp = 0;
/* % in string ? */
if ((ap = strchr (ap, '%')) != (char *)NULL)
*ap = 0;
/* Check the file name */
if ((stat (sp, &st) != -1) && (st.st_mtime > last) && st.st_size)
{
if (ap != (char *)NULL)
fprintf (stderr, "%s\n", ap + 1);
else
fputs (ymail, stderr);
}
/* Restore the % */
if (ap != (char *)NULL)
*ap = '%';
/* Restore the semi-colon and find the next one */
*cp = ';';
sp = cp + 1;
}
}
/* Just check MAIL */
else if ((mail != null) && (stat (mail, &st) != -1) &&
(st.st_mtime > last) && st.st_size)
fputs (ymail, stderr);
/* Save the last check time */
last = current;
}
/*
* Preprocess Argv to get handle of options in the format /x
*
* Some programs invoke the shell using / instead of - to mark the options.
* We need to convert to -. Also /c is a special case. The rest of the
* command line is the command to execute. So, we get the command line
* from the original buffer instead of argv array.
*/
static void near Pre_Process_Argv (char **argv, int *argc1)
{
#ifdef OS2
char *ocl = (char far *)((((long)_aenvseg) << 16) + _acmdln);
int argc = 1;
ocl += strlen (ocl) + 1;
#else
char *ocl = (char far *)((((long)_psp) << 16) + 0x081L);
int argc = 1;
#endif
/* Check for these options */
while ((*++argv != (char *)NULL) && (strlen (*argv) == 2) &&
(**argv == CHAR_UNIX_DIRECTORY))
{
argc++;
*strlwr (*argv) = '-';
/* Get the original information from the command line */
if ((*argv)[1] == 'c')
{
while ((*ocl != CHAR_UNIX_DIRECTORY) && (*(ocl + 1) != 'c') &&
(*ocl) && (*ocl != CHAR_RETURN))
++ocl;
if (*ocl != CHAR_UNIX_DIRECTORY)
continue;
/* Find the start of the string */
ocl += 2;
while (isspace (*ocl) && (*ocl != CHAR_RETURN))
++ocl;
if (*ocl == CHAR_RETURN)
continue;
/* Found the start. Set up next parameter and ignore the rest */
if (*(argv + 1) == (char *)NULL)
continue;
argc++;
*(argv + 1) = ocl;
*(argv + 2) = (char *)NULL;
*argc1 = argc;
if ((ocl = strchr (ocl, CHAR_RETURN)) != (char *)NULL)
*ocl = 0;
return;
}
}
}
/*
* Convert path format to/from UNIX
*/
char *ConvertPathToFormat (char *path, char in, char out)
{
char *s = path;
while ((path = strchr (path, in)) != (char *)NULL)
*path = out;
return s;
}
/* Load profiles onto I/O Stack */
static void near LoadTheProfileFiles (void)
{
char *name = BuildFileName ("profile"); /* Set up home profile */
char *Pname = "x:/etc/profile";
InteractiveFlag = TRUE;
AddToStackForExection (name);
ReleaseMemoryCell ((void *)name);
*Pname = (char)(GetRootDiskDrive () + 'a' - 1);
AddToStackForExection (Pname);
}
/*
* Convert Unix PATH to MSDOS PATH
*/
static void near ConvertUnixPathToMSDOS (void)
{
char *cp = GetVariableAsString (PathLiteral, FALSE);
char *scp = cp;
int colon = 0;
/* If there is a semi-colon or a backslash, we assume this is a DOS format
* path
*/
if ((strchr (cp, ';') != (char *)NULL) ||
(strchr (cp, '\\') != (char *)NULL))
return;
/* Count the number of colons */
while ((cp = strchr (cp, ':')) != (char *)NULL)
{
++colon;
++cp;
}
/* If there are no colons or there is one colon as the second character, it
* is probably an MSDOS path
*/
cp = scp;
if ((colon == 0) || ((colon == 1) && (*(cp + 1) == ':')))
return;
/* Otherwise, convert all colons to semis */
while ((cp = strchr (cp, ':')) != (char *)NULL)
*(cp++) = ';';
}
/* Generate a file name from a directory and name. Return null if an error
* occurs or some malloced space containing the file name otherwise
*/
char *BuildFileName (char *name)
{
char *dir = GetVariableAsString (HomeVariableName, FALSE);
char *cp;
/* Get some space */
if ((cp = AllocateMemoryCell (strlen (dir) + strlen (name) + 2))
== (char *)NULL)
return null;
/* Addend the directory and a / if the directory does not end in one */
strcpy (cp, dir);
if (cp[strlen (cp) - 1] != CHAR_UNIX_DIRECTORY)
strcat (cp, DirectorySeparator);
/* Append the file name */
return strcat (cp, name);
}
/* Clear prompts */
static void near ClearUserPrompts (void)
{
ClearVariableStatus (PS1, STATUS_EXPORT);
ClearVariableStatus (PS2, STATUS_EXPORT);
ClearVariableStatus (PS3, STATUS_EXPORT);
SetVariableFromString (PS1, null);
SetVariableFromString (PS2, null);
SetVariableFromString (PS3, null);
}
/* Process setting of SECONDS and RANDOM environment variables */
static void near SecondAndRandomEV (char *name, long val)
{
if (!strcmp (name, SecondsVariable) &&
!(DisabledVariables & DISABLE_SECONDS))
{
SecondsOffset = (time_t)val;
ShellStartTime = time ((time_t *)NULL);
}
else if (!strcmp (name, RandomVariable) &&
!(DisabledVariables & DISABLE_RANDOM))
srand ((int)val);
}
/*
* Set up the Window name. Give up if it does not work.
*/
#ifdef OS2
void SetWindowName (void)
{
HSWITCH hswitch;
SWCNTRL swctl;
PIDINFO PidInfo;
if (DosGetPID (&PidInfo))
return;
if (!(hswitch = WinQuerySwitchHandle (0, PidInfo.pid)))
return;
if (WinQuerySwitchEntry (hswitch, &swctl))
return;
strcpy (swctl.szSwtitle, "MS Shell");
WinChangeSwitchEntry (hswitch, &swctl);
}
/*
* In OS/2, check for terminated processes
*/
static void near CheckForTerminatedProcess (void)
{
RESULTCODES rescResults;
PID pidProcess;
char *s;
while (TRUE)
{
if (DosCwait (DCWA_PROCESSTREE, DCWW_NOWAIT, &rescResults,
&pidProcess, 0))
return;
DeleteJob (pidProcess);
switch (rescResults.codeTerminate)
{
case TC_EXIT:
s = "Normal Exit";
break;
case TC_HARDERROR:
s = "Hard error";
break;
case TC_TRAP:
s = "Trapped";
break;
case TC_KILLPROCESS:
s = "Killed";
break;
default:
s = "Unknown";
break;
}
fprintf (stderr, "Process %d terminated - %s (%d)\n", pidProcess, s,
rescResults.codeTerminate);
}
}
#endif
/*
* Set up the Parameter Environment variables
*/
static void near SetUpParameterEV (int argc, char **argv, char *name)
{
Word_B *wb = (Word_B *)NULL;
char *Value;
int i;
if ((Value = StringSave (name)) == null)
{
fprintf (stderr, BasicErrorMessage, ShellNameLiteral, Outofmemory1);
return;
}
wb = AddParameter (Value, wb, ShellNameLiteral);
for (i = 1; i < argc; ++i)
{
if ((!AssignVariableFromString (argv[i])) && (wb != (Word_B *)NULL))
{
if ((Value = StringSave (argv[i])) != null)
wb = AddParameter (Value, wb, ShellNameLiteral);
else
{
fprintf (stderr, BasicErrorMessage, ShellNameLiteral,
Outofmemory1);
return;
}
}
}
if (wb != (Word_B *)NULL)
wb = AddParameter ((char *)NULL, wb, ShellNameLiteral);
}
/*
* Update the Seconds and Random variables
*/
void HandleSECONDandRANDOM (void)
{
if (!(DisabledVariables & DISABLE_SECONDS))
LookUpVariable (SecondsVariable, TRUE);
if (!(DisabledVariables & DISABLE_RANDOM))
LookUpVariable (RandomVariable, TRUE);
}
/*
* Set the status of an environment variable
*/
void SetVariableStatus (char *name, int flag)
{
VariableList *vp = LookUpVariable (name, TRUE);
if (isalpha (*vp->name)) /* not an internal symbol ($# etc) */
vp->status |= flag;
}
/*
* Set the status of an environment variable
*/
void ClearVariableStatus (char *name, int flag)
{
VariableList *vp = LookUpVariable (name, TRUE);
if (isalpha (*vp->name)) /* not an internal symbol ($# etc) */
vp->status &= ~flag;
}
/*
* Check allowed to set variable
*/
static bool near AllowedToSetVariable (VariableList *vp)
{
if (vp->status & STATUS_READONLY)
{
ShellErrorMessage ("%s is read-only", vp->name);
return FALSE;
}
/* Check for $PATH, $SHELL or $ENV reset in restricted shell */
if ((!strcmp (vp->name, PathLiteral) || !strcmp (vp->name, ENVVariable) ||
!strcmp (vp->name, ShellVariableName)) &&
CheckForRestrictedShell (PathLiteral))
return FALSE;
return TRUE;
}
/*
* Set up a variable from a string
*/
void SetVariableFromString (char *name, char *val)
{
VariableList *vp = LookUpVariable (name, TRUE);
char *xp = null;
long nval;
/* Check if allowed to set variable */
if (!AllowedToSetVariable (vp))
return;
/* If we change the PATH to a new value, we need to untrack some aliases */
if (!strcmp (name, PathLiteral) && strcmp (vp->value, val))
UnTrackAllAliases ();
CheckOPTIND (name, atol (val));
/* Save the new value */
if ((!(vp->status & STATUS_INTEGER)) && (val != null) && strlen (val) &&
((xp = StringSave (val = SuppressSpacesZeros (vp, val))) == null))
return;
/* Free the old value if appropriate */
if (vp->value != null)
ReleaseMemoryCell ((void *)vp->value);
vp->value = null;
/* String value? */
if (!(vp->status & STATUS_INTEGER))
{
vp->value = xp;
if (!vp->width)
vp->width = strlen (val);
}
/* No - Number value */
else if (!ValidMathsExpression (val, &nval))
SetUpANumericValue (vp, nval, -1);
/* Check to see if it should be exported */
if (FL_TEST ('a'))
vp->status |= STATUS_EXPORT;
/* Convert UNIX to DOS for PATH variable */
if (!strcmp (name, PathLiteral))
ConvertUnixPathToMSDOS ();
}
/*
* Set a variable from a numeric
*/
void SetVariableFromNumeric (char *name, long value)
{
VariableList *vp = LookUpVariable (name, TRUE);
char NumericBuffer[20];
/* Check if allowed to set variable */
if (!AllowedToSetVariable (vp))
return;
CheckOPTIND (name, value);
if (!(vp->status & STATUS_INTEGER))
{
sprintf (NumericBuffer, "%ld", value);
SetVariableFromString (name, NumericBuffer);
}
/* Save the integer value */
else
SetUpANumericValue (vp, value, -1);
}
/*
* Get variable as a numeric
*/
long GetVariableAsNumeric (char *name)
{
VariableList *vp = LookUpVariable (name, FALSE);
if (vp->status & STATUS_INTEGER)
return vp->nvalue;
else
return atol (vp->value);
}
/*
* Get variable as a numeric
*/
char *GetVariableAsString (char *name, bool Format)
{
VariableList *vp = LookUpVariable (name, FALSE);
char *Value = vp->value;
char *xp;
size_t len;
char *NumericBuffer;
if (vp->status & STATUS_INTEGER)
{
if ((NumericBuffer = GetAllocatedSpace (40)) == (char *)NULL)
return null;
if (vp->base != 10)
{
sprintf (NumericBuffer, "[%d]", vp->base);
xp = NumericBuffer + strlen (NumericBuffer);
}
else
xp = NumericBuffer;
ltoa (vp->nvalue, xp, vp->base);
return NumericBuffer;
}
/* Handle a string variable, if no formating required, return it */
if (!Format)
return vp->value;
/* Left justify ? */
if (vp->status & STATUS_LEFT_JUSTIFY)
{
xp = SuppressSpacesZeros (vp, Value);
if ((Value = GetAllocatedSpace (vp->width + 1)) == (char *)NULL)
return null;
memset (Value, ' ', vp->width);
Value[vp->width] = '\0';
if ((len = strlen (xp)) > vp->width)
len = vp->width;
memcpy (Value, xp, len);
}
/* Right justify ? */
else if (vp->status & (STATUS_RIGHT_JUSTIFY | STATUS_ZERO_FILL))
{
if ((xp = GetAllocatedSpace (vp->width + 1)) == (char *)NULL)
return null;
if ((len = strlen (Value)) < vp->width)
{
memset (xp,
((vp->status & STATUS_ZERO_FILL) &&
(isdigit (*Value))) ? '0' : ' ',
vp->width);
memcpy (xp + (vp->width - len), Value, len);
}
else
memcpy (xp, Value + vp->width - len, vp->width);
*(xp + vp->width) = 0;
Value = xp;
}
/* Handle upper and lower case conversions */
if (vp->status & STATUS_LOWER_CASE)
Value = strlwr (StringCopy (Value));
if (vp->status & STATUS_UPPER_CASE)
Value = strupr (StringCopy (Value));
return Value;
}
/*
* Set up a numeric value
*/
static void near SetUpANumericValue (VariableList *vp, long value, int base)
{
vp->nvalue = value;
vp->status |= STATUS_INTEGER;
if (vp->base == 0)
vp->base = (base > 1) ? base
: ((LastNumberBase != -1) ? LastNumberBase
: 10);
if (vp->value != null)
ReleaseMemoryCell ((void *)vp->value);
vp->value = null;
}
/*
* Suppress leading spaces and zeros
*/
static char * near SuppressSpacesZeros (VariableList *vp, char *value)
{
/* Suppress blanks and zeros */
if (vp->status & STATUS_LEFT_JUSTIFY)
{
while (*value == ' ')
value++;
if (vp->status & STATUS_ZERO_FILL)
{
while (*value == '0')
value++;
}
}
return value;
}
/*
* Check to see if a reset of CheckOPTIND has occured
*/
static void near CheckOPTIND (char *name, long value)
{
if ((value == 1) && (!(DisabledVariables & DISABLE_OPTIND)) &&
(strcmp (OptIndVariable, name) == 0))
ResetGetoptsValues (FALSE);
}
/*
* Initialise the Integer variables by creating them
*/
static void near CreateIntegerVariables (void)
{
struct ShellVariablesInit *wp = InitialiseShellVariables;
while (wp->Name != (char *)NULL)
{
SetVariableStatus (wp->Name, wp->Status);
if (wp->CValue != null)
SetVariableFromString (wp->Name, wp->CValue);
wp++;
}
}